#include "Md2.h"

VERTEX::VERTEX(float x, float y, float z)
{
	vertex[0]=x;
	vertex[1]=y;
	vertex[2]=z;
}

void VERTEX::SendToOGL(void)
{	
	glVertex3fv(vertex);	
}

inline VERTEX VERTEX::operator+ (const VERTEX &v)
{
	VERTEX result(0.0f, 0.0f, 0.0f);

	result.vertex[0]= vertex[0] + v.vertex[0];
	result.vertex[1]= vertex[1] + v.vertex[1];
	result.vertex[2]= vertex[2] + v.vertex[2];

	return result;
}

inline VERTEX VERTEX::operator- (const VERTEX &v)
{
	VERTEX result(0.0f, 0.0f, 0.0f);

	result.vertex[0]= vertex[0] - v.vertex[0];
	result.vertex[1]= vertex[1] - v.vertex[1];
	result.vertex[2]= vertex[2] - v.vertex[2];

	return result;
}

///////////////////////////

bool MD2::Load(char* filename)
{
	FILE* file;
	MD2_HEADER header;

	if((file= fopen(filename, "rb"))==NULL)
	 return false;
	
	fread(&header, sizeof(MD2_HEADER), 1, file);

	if(header.magic!= 844121161)
	 return false;

	if(header.version!=8)
	 return false;
	

	frames= new char[header.frameSize*header.numFrames];
	
	if(frames==NULL)
		return false;

	fseek(file, header.offsetFrames, SEEK_SET);
	fread(frames, header.frameSize*header.numFrames, 1, file);

	glCommands= new long [header.numGlCommands*sizeof(long)];

	if(glCommands==NULL)
		return false;

	fseek(file,		  header.offsetGlCommands, SEEK_SET);
	fread(glCommands, header.numGlCommands*sizeof(long), 1, file);

	numFrames	 = header.numFrames;
	numGlCommands= header.numGlCommands;
	frameSize	 = header.frameSize;
	numTriangles = header.numTriangles;

	fclose(file);	

	return true;
}

void MD2::Render(int numFrame)
{
	static MD2_MODELVERTEX vertList[100];

	MD2_FRAME_PTR	currentFrame;
	VERTEX			v1;
	long*			command;
    float			texcoord[2];
	int				loop;
	int				vertIndex;
	int				type;
	int				numVertex;
	int				index;

	//Get the current frame and gl command information
	currentFrame= (MD2_FRAME*) ((char*)frames+frameSize*numFrame);
	command		= glCommands;

	//Make sure that the command doesn't equal 0, and if it doesn't lets start rendering!
	while((*command)!=0)
		{
		if(*command>0)	//This is a triangle strip
			{
			numVertex= *command; 
			command++; 
			type= 0;
			}
		else			//This is a triangle fan
			{
			numVertex= - *command; 
			command++; 
			type= 1;
			}

		if(numVertex<0)
			numVertex= -numVertex;

		index= 0;
		//Fill the vertex list information
		for(loop=0; loop<numVertex; loop++)
			{
			vertList[index].u= *((float*)command); 
			command++;
			vertList[index].v= *((float*)command); 
			command++;

			vertIndex= *command; 
			command++;

			vertList[index].x= ( (currentFrame->vertices[vertIndex].vertex[0]* 
								  currentFrame->scale[0])+ 
								  currentFrame->translate[0]);
			vertList[index].z= -((currentFrame->vertices[vertIndex].vertex[1]* 
								  currentFrame->scale[1])+ 
								  currentFrame->translate[1]);
			vertList[index].y= ( (currentFrame->vertices[vertIndex].vertex[2]*
								  currentFrame->scale[2])+
								  currentFrame->translate[2]);
			index++;
			}

		//If the .md2 was optimized for use with triangle strips...
		if(type==0)
		{
			glBegin(GL_TRIANGLE_STRIP);
			for(loop=0; loop<index; loop++)
			{
				v1.vertex[0]=(vertList[loop].x);
				v1.vertex[1]=(vertList[loop].y);
				v1.vertex[2]=(vertList[loop].z);

                texcoord[0]= vertList[loop].u;
				texcoord[1]= vertList[loop].v;

				glTexCoord2fv(texcoord);
				glVertex3fv(v1.vertex);
			}
			glEnd();
		}

		//If the .md2 was made for use with triangle fans...
		else
		{
			glBegin(GL_TRIANGLE_FAN);
			for(loop=0; loop<index; loop++)
			{
				v1.vertex[0]=(vertList[loop].x);
				v1.vertex[1]=(vertList[loop].y);
				v1.vertex[2]=(vertList[loop].z);

                texcoord[0]= vertList[loop].u;
				texcoord[1]= vertList[loop].v;

				glTexCoord2fv(texcoord);
                v1.SendToOGL();
            }
			glEnd();
		}

		}
}

void MD2::Animate(int startFrame, int EndFrame, float percent)
{
	if(currentFrame==-1 || currentFrame>endFrame)
		{
		currentFrame= startFrame;
		nextFrame	= currentFrame+1;
		endFrame	= EndFrame;
		}

	if(interpolation>1.0f)
		{
		interpolation= 0.0f;
		currentFrame = nextFrame;
		nextFrame	 = currentFrame+1;
		}

	interpolation+=percent;
	Render(currentFrame);
}

void MD2::SetState(int state)
{	
	switch(state)
		{
		case IDLE1:
			stateStart	 = IDLE1_START;
			stateEnd	 = IDLE1_END;
			currentFrame =-1;
			break;
		case RUN:
			stateStart	 = RUN_START;
			stateEnd	 = RUN_END;
			currentFrame =-1;
			break;
		case SHOT_STAND:
			stateStart	 = SHOT_STAND_START;
			stateEnd	 = SHOT_STAND_END;
			currentFrame =-1;
			break;
		case SHOT_SHOULDER:
			stateStart	 = SHOT_SHOULDER_START;
			stateEnd	 = SHOT_SHOULDER_END;
			currentFrame =-1;
			break;
		case JUMP:
			stateStart	 = JUMP_START;
			stateEnd	 = JUMP_END;
			currentFrame =-1;
			break;
		case IDLE2:
			stateStart	 = IDLE2_START;
			stateEnd	 = IDLE2_END;
			currentFrame =-1;
			break;
		case SHOT_FALLDOWN:
			stateStart	 = SHOT_FALLDOWN_START;
			stateEnd	 = SHOT_FALLDOWN_END;
			currentFrame =-1;
			break;
		case IDLE3:
			stateStart	 = IDLE3_START;
			stateEnd	 = IDLE3_END;
			currentFrame =-1;
			break;
		case IDLE4:
			stateStart	 = IDLE4_START;
			stateEnd	 = IDLE4_END;
			currentFrame =-1;
			break;
		case CROUCH:
			stateStart	 = CROUCH_START;
			stateEnd	 = CROUCH_END;
			currentFrame =-1;
			break;
		case CROUCH_CRAWL:
			stateStart	 = CROUCH_CRAWL_START;
			stateEnd	 = CROUCH_CRAWL_END;
			currentFrame =-1;
			break;
		case CROUCH_IDLE:
			stateStart	 = CROUCH_IDLE_START;
			stateEnd	 = CROUCH_IDLE_END;
			currentFrame =-1;
			break;
		case CROUCH_DEATH:
			stateStart	 = CROUCH_DEATH_START;
			stateEnd	 = CROUCH_DEATH_END;
			currentFrame =-1;
			break;
		case DEATH_FALLBACK:
			stateStart	 = DEATH_FALLBACK_START;
			stateEnd	 = DEATH_FALLBACK_END;
			currentFrame =-1;
			break;
		case DEATH_FALLFORWARD:
			stateStart	 = DEATH_FALLFORWARD_START;
			stateEnd	 = DEATH_FALLFORWARD_END;
			currentFrame =-1;
			break;
		case DEATH_FALLBACKSLOW:
			stateStart	 = DEATH_FALLBACKSLOW_START;
			stateEnd	 = DEATH_FALLBACKSLOW_END;
			currentFrame =-1;
			break;
		}	
}
